package com.iwhalecloud.bss.kite.cucc.service.pay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.iwhalecloud.bss.hsf.baseinf.service.InfExchangeService;
import com.iwhalecloud.bss.kite.common.constant.ErrorConsts;
import com.iwhalecloud.bss.kite.common.util.KiteListUtils;
import com.iwhalecloud.bss.kite.common.util.KiteMapUtils;
import com.iwhalecloud.bss.kite.common.util.KiteObjectUtils;
import com.iwhalecloud.bss.kite.common.util.KiteStringUtils;
import com.iwhalecloud.bss.kite.cucc.client.api.inf.IInfServiceBus;
import com.iwhalecloud.bss.kite.cucc.client.api.order.IOrderService;
import com.iwhalecloud.bss.kite.cucc.client.api.pay.IPaymentService;
import com.iwhalecloud.bss.kite.cucc.client.dto.order.OrderInfo;
import com.iwhalecloud.bss.kite.cucc.client.dto.order.OrderInfoListReq;
import com.iwhalecloud.bss.kite.cucc.client.dto.order.OrderInfoReq;
import com.iwhalecloud.bss.kite.cucc.client.dto.order.OrderListInfo;
import com.iwhalecloud.bss.kite.cucc.client.dto.org.GovUserCodeRspDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.org.QryGovUserCodeReqDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.org.UserInfoDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pay.ParaDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pay.PayCommonReqDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pay.PayCommonRspDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pay.PayContentReqDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pay.PayContentRspDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pay.PayInfoConfirmDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pay.PayStatusRspDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pay.ResaleOrderRequest;
import com.iwhalecloud.bss.kite.cucc.client.dto.pos.KitePosAttrDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pos.KitePosDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pos.KitePosDetailDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pos.KitePosGoodsDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pos.KitePosGoodsFeeDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pos.KitePosPaymentDTO;
import com.iwhalecloud.bss.kite.cucc.client.dto.pos.KitePosProductDTO;
import com.iwhalecloud.bss.kite.cucc.client.service.cache.ILocalRefreshCacheService;
import com.iwhalecloud.bss.kite.cucc.common.callclient.HttpsCallClient;
import com.iwhalecloud.bss.kite.cucc.common.constant.LocalCallClientEnum;
import com.iwhalecloud.bss.kite.cucc.common.constant.LocalKeyConsts;
import com.iwhalecloud.bss.kite.cucc.common.util.DateFormatUtils;
import com.iwhalecloud.bss.kite.cucc.common.util.DecimalCalc;
import com.iwhalecloud.bss.kite.cucc.dataservice.cache.OneOffFeeExtOrderInfoCache;
import com.iwhalecloud.bss.kite.cucc.dataservice.cache.OneOffFeeOrderNoCache;
import com.iwhalecloud.bss.kite.cucc.dataservice.cache.OneOffFeePayUrlCache;
import com.iwhalecloud.bss.kite.cucc.service.inf.InfServiceHelper;
import com.iwhalecloud.bss.kite.cucc.service.util.ChangeTypeUtils;
import com.iwhalecloud.bss.kite.cucc.service.util.LocalTokenUtil;
import com.iwhalecloud.bss.kite.cucc.service.util.PayUtil;
import com.iwhalecloud.bss.kite.dataservice.cache.DcPublicCache;
import com.iwhalecloud.bss.kite.dataservice.cache.OfferCache;
import com.iwhalecloud.bss.kite.dataservice.cache.ProductCache;
import com.iwhalecloud.bss.kite.dataservice.entity.DcPublic;
import com.iwhalecloud.bss.kite.dataservice.entity.spec.Offer;
import com.iwhalecloud.bss.kite.dataservice.entity.spec.Product;
import com.ztesoft.bss.common.bo.PostInfo;
import com.ztesoft.bss.common.bo.UserInfo;
import com.ztesoft.bss.common.util.ContextUtil;
import com.ztesoft.bss.common.util.SpringUtil;
import com.ztesoft.zsmart.core.log.ZSmartLogger;
import org.apache.commons.collections.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.iwhalecloud.bss.kite.dataservice.cache.ProdFeeCache;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletionService;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

@Service
public class PaymentService implements IPaymentService {

    private static final ZSmartLogger LOGGER = ZSmartLogger.getLogger(PaymentService.class);

    @Value("${interface.call.mode:hsf}")
    private String mode;

    @Autowired
    private IOrderService orderService;

    @Autowired
    private IInfServiceBus infServiceBus;

    @Autowired
    private ILocalRefreshCacheService localRefreshCacheService;

    private static final String PAY_ORDER_OP_CODE = "json_newbuy_settleCenter_cashierPayment";
    private static final String PAY_STATUS_OP_CODE = "json_newbuy_settleCenter_standardPayStatusQuery";
    private static Map<String, Object> STATUS_MAP = null;

    static {
        STATUS_MAP = new HashMap<>();
        STATUS_MAP.put("SUCCESS", "支付成功");
        STATUS_MAP.put("FAIL", "支付失败");
        STATUS_MAP.put("PAYING", "未支付");
        STATUS_MAP.put("REFUNDING", "退款中");
        STATUS_MAP.put("REFUND_SUCCESS", "退款成功");
        STATUS_MAP.put("REFUND_FAIL", "退款失败");
    }

    /**
     * 支付下单接口（返回收银台URL）
     *
     * @param orderInfo
     * @return
     */
    @Override
    public PayContentRspDTO payOrder(OrderInfo orderInfo) {
        UserInfo userInfo = ContextUtil.getLoginInfo().getUserInfo();
        String provinceCode = userInfo.getPostProvinceNbr();
        String cityCode = userInfo.getPostLanId();
        Long districtCode = userInfo.getPostRegionId();
        String departId = getDepartId();
        String staffId = KiteMapUtils.getString(userInfo.getExtParams(), "accountId"); // 集客工号
        DcPublic oneOffFeeConfig = this.getOneOffFeeConfig();
        String payBusinessId = oneOffFeeConfig.getCodea();
        String payBusiId = oneOffFeeConfig.getCodeb();
        String payInModeCode = oneOffFeeConfig.getCodec();
        String payMerchantId = oneOffFeeConfig.getCoded();
        String payRollbackUrl = oneOffFeeConfig.getCodee();
        // 初始订单流水
        String orderId = orderInfo.getOrderId();
        // 获取订单详情
        KitePosDTO kitePosDTO = OneOffFeeExtOrderInfoCache.get(orderId);
        if (Objects.isNull(kitePosDTO)) {
            kitePosDTO = getOrderDetail(orderId);
        }
        // 校验金额是否和趸交资费相等
        if (!this.isEqualAmount(orderInfo.getPayMoney(), kitePosDTO.getGoods())) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("趸交资费总价与趸交资费列表总价不符，请联系运维人员进行处理。");
        }
        // 如果已经下过单了，并且缓存还有效
        String urlCache = OneOffFeePayUrlCache.get(orderId);
        if (KiteStringUtils.isNotEmpty(urlCache)) {
            return getPayContentRsp(orderId, urlCache);
        }
        String newOrderId = getNewOrderId(orderId);
        // 请求参数内容
        PayContentReqDTO payContentReqDTO = new PayContentReqDTO();
        payContentReqDTO.setOrderType("01");    // 01 支付
        payContentReqDTO.setReqWay("1");    // 1 PC支付
        payContentReqDTO.setOutOrderId(newOrderId);
        payContentReqDTO.setMerchantId(payMerchantId);
        payContentReqDTO.setBusinessId(payBusinessId);
        payContentReqDTO.setRealFee(orderInfo.getPayMoney());
        payContentReqDTO.setDetailName("-1");
        payContentReqDTO.setDistrict("-1");
        payContentReqDTO.setNotifyUrl(payRollbackUrl);
        payContentReqDTO.setCreateOperId(staffId);
        payContentReqDTO.setProvince(provinceCode);
        payContentReqDTO.setCity(cityCode);
        if (Objects.nonNull(districtCode)) {
            payContentReqDTO.setDistrict(String.valueOf(districtCode));
        }
        String staffCode = KiteMapUtils.getString(userInfo.getExtParams(), "account"); // 集客工号
        Map<String, Object> channelInfo = infServiceBus.queryChannelByUserInfo(staffId, staffCode, provinceCode);
        if (KiteMapUtils.isEmpty(channelInfo)) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("根据集客工号【" + staffId + "】未查询到其对应的渠道信息");
        }
        // 发展人渠道
//        KitePosAttrDTO channelAttr = kitePosDTO.getOrder().getAttrs().stream().filter(attr -> LocalKeyConsts.DEVPERSON_INFO_CHANNELID.equals(attr.getCode())).findFirst().orElse(null);
//        String channelId = Optional.ofNullable(channelAttr).map(KitePosAttrDTO::getValue).orElse(null);
        payContentReqDTO.setChannelId(KiteMapUtils.getString(channelInfo, "CHNL_CODE"));
        // 发展人渠道名称
//        KitePosAttrDTO channelNameAttr = kitePosDTO.getOrder().getAttrs().stream().filter(attr -> LocalKeyConsts.DEVPERSON_INFO_CHANNELNAME.equals(attr.getCode())).findFirst().orElse(null);
//        String channelName = Optional.ofNullable(channelNameAttr).map(KitePosAttrDTO::getValue).orElse(null);
        payContentReqDTO.setChannelName(KiteMapUtils.getString(channelInfo, "CHNL_NAME"));
        // 省分营业厅标识部门编码
        payContentReqDTO.setDepartId(departId);
        payContentReqDTO.setRecvSiteType("01"); // 01 自有实体营业厅
        payContentReqDTO.setOperTypeCode("0");  // 0 订单受理
        // 源品牌编码
        payContentReqDTO.setProBrandCode(Optional.ofNullable(OfferCache.get(getOfferId(kitePosDTO))).map(Offer::getBrandId).orElse(null));
        payContentReqDTO.setProResTypeCode("-1");
        payContentReqDTO.setProResKindCode("-1");
        payContentReqDTO.setNetTypeCode(Optional.ofNullable(ProductCache.get(getBaseProdId(kitePosDTO))).map(Product::getServiceTypeCode).orElse(null));
        // 保留字段
        ParaDTO paraDTO = new ParaDTO();
        paraDTO.setParaId("order_id");
        paraDTO.setParaValue(orderId);
        payContentReqDTO.setPara(Lists.newArrayList(paraDTO));
        // 对请求参数内容进行加密
        String content = "";
        try {
            content = PayUtil.getEncryptContentForHump(payContentReqDTO);
        } catch (Exception e) {
            ErrorConsts.INF_INVOKE_ERROR.throwOut(PAY_ORDER_OP_CODE, "支付下单接口",
                "加密异常：" + e.getMessage());
        }
        if (KiteStringUtils.isEmpty(content)) {
            ErrorConsts.INF_INVOKE_ERROR.throwOut(PAY_ORDER_OP_CODE, "支付下单接口",
                "支付下单接口请求内容为空！");
        }
        // 请求参数
        PayCommonReqDTO payCommonReqDTO = new PayCommonReqDTO();
        payCommonReqDTO.setBusiId(payBusiId);
        payCommonReqDTO.setInModeCode(payInModeCode);
        payCommonReqDTO.setStaffId(staffId);
        payCommonReqDTO.setContent(content);
        // 调接口
        Map<String, Object> cashierPayment = PayUtil.HumpToLine(payCommonReqDTO);
        Map<String, Object> uniBssBody = new HashMap();
        uniBssBody.put("CASHIER_PAYMENT_REQ", cashierPayment);
        Map<String, Object> param = new HashMap<>();
        param.put("UNI_BSS_BODY", uniBssBody);
        param.put("OP_CODE", PAY_ORDER_OP_CODE);
        InfServiceHelper.adaptGray(param, cashierPayment);
        String resultStr;
        if (KiteStringUtils.isEqual(mode, "http")) {
            resultStr = HttpsCallClient.postStringWithObject(LocalCallClientEnum.INF_COMMON_URL, param, LocalTokenUtil.getHeaderMap());
        } else {
            InfExchangeService infExchangeService = SpringUtil.getBean("infExchangeService");
            resultStr = infExchangeService.exchange(new JSONObject(param));
        }
        if (KiteStringUtils.isEmpty(resultStr)) {
            ErrorConsts.INF_INVOKE_ERROR.throwOut(PAY_ORDER_OP_CODE, "支付下单接口",
                "支付下单失败");
        }
        // 返回内容
        PayContentRspDTO payContentRspDTO = new PayContentRspDTO();
        Map respMap = JSON.parseObject(resultStr, Map.class);
        Map uniBssBodyResult = MapUtils.getMap(respMap, "UNI_BSS_BODY");
        PayCommonRspDTO payCommonRspDTO = JSONObject.parseObject(JSON.toJSONString(uniBssBodyResult.get("CASHIER_PAYMENT_RSP")), PayCommonRspDTO.class);
        if ("0000".equals(payCommonRspDTO.getRspCode())) {
            // 解密
            String decodeContent = "";
            try {
                decodeContent = PayUtil.getDecryptContent(payCommonRspDTO.getContent());
                if (!PayUtil.signValid(decodeContent)) {
                    ErrorConsts.INF_INVOKE_ERROR.throwOut(PAY_ORDER_OP_CODE, "支付下单接口", "签名串校验异常");
                }
            } catch (Exception e) {
                ErrorConsts.INF_INVOKE_ERROR.throwOut(PAY_ORDER_OP_CODE, "支付下单接口",
                    "解密异常：" + e.getMessage());
            }
            payContentRspDTO = JSONObject.parseObject(decodeContent, PayContentRspDTO.class);
            if (Objects.nonNull(payContentRspDTO) && Objects.nonNull(payContentRspDTO.getUrl())) {
                OneOffFeePayUrlCache.put(orderId, payContentRspDTO.getUrl());
                OneOffFeeOrderNoCache.getInst().defaultPut(orderId, newOrderId);
                OneOffFeeExtOrderInfoCache.getInst().defaultDelete(orderId);
            }
        } else {
            ErrorConsts.INF_INVOKE_ERROR.throwOut(PAY_ORDER_OP_CODE, "支付下单接口",
                "支付下单失败:" + payCommonRspDTO.getRspName());
        }
        return payContentRspDTO;
    }

    /**
     * 获取新流水号，orderId为初始订单号，如果未下过单，则返回初始订单号
     * @param orderId
     * @return
     */
    private String getNewOrderId(String orderId) {
        String oldOrderId = OneOffFeeOrderNoCache.get(orderId);
        // 不为空说明下单成功过
        if (KiteStringUtils.isNotEmpty(oldOrderId)) {
            return PayUtil.getNextSequence(oldOrderId);
        }
        return orderId;
    }

    /**
     * 支付结果查询
     *
     * @param outOrderId
     * @return
     */
    @Override
    public PayStatusRspDTO qryPayStatus(String outOrderId) {
        String staffId = KiteMapUtils.getString(ContextUtil.getLoginInfo().getUserInfo().getExtParams(), "accountId"); // 集客工号
        DcPublic oneOffFeeConfig = this.getOneOffFeeConfig();
        String payBusiId = oneOffFeeConfig.getCodeb();
        String payInModeCode = oneOffFeeConfig.getCodec();
        String orderId = OneOffFeeOrderNoCache.get(outOrderId);
        if (KiteStringUtils.isEmpty(orderId)) {
            orderId = outOrderId;
        }
        // 请求内容
        Map<String, Object> content = new HashMap<>();
        content.put("OUT_ORDER_ID", orderId);
        content.put("REQ_WAY", "1");
        content.put("CREATE_OPER_ID", staffId);
        content.put("ORDER_TYPE", "03");    // 03订单查询
        // 内容加密处理
        String encryptContent = "";
        try {
            encryptContent = PayUtil.getEncryptContent(content);
        } catch (Exception e) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("支付结果查询接口加密异常：" + e.getMessage());
        }
        if (KiteStringUtils.isEmpty(encryptContent)) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("支付结果查询接口请求内容为空！");
        }
        // 请求参数
        PayCommonReqDTO payCommonReqDTO = new PayCommonReqDTO();
        payCommonReqDTO.setStaffId(staffId);
        payCommonReqDTO.setInModeCode(payInModeCode);
        payCommonReqDTO.setBusiId(payBusiId);
        payCommonReqDTO.setContent(encryptContent);
        // 调接口
        Map<String, Object> payStatusQueryReq = PayUtil.HumpToLine(payCommonReqDTO);
        Map<String, Object> uniBssBody = new HashMap();
        uniBssBody.put("STANDARD_PAY_STATUS_QUERY_REQ", payStatusQueryReq);
        Map<String, Object> param = new HashMap<>();
        param.put("UNI_BSS_BODY", uniBssBody);
        param.put("OP_CODE", PAY_STATUS_OP_CODE);
        InfServiceHelper.adaptGray(param, payStatusQueryReq);
        String resultStr;
        if (KiteStringUtils.isEqual(mode, "http")) {
            resultStr = HttpsCallClient.postStringWithObject(LocalCallClientEnum.INF_COMMON_URL, param, LocalTokenUtil.getHeaderMap());
        } else {
            InfExchangeService infExchangeService = SpringUtil.getBean("infExchangeService");
            resultStr = infExchangeService.exchange(new JSONObject(param));
        }
        if (KiteStringUtils.isEmpty(resultStr)) {
            ErrorConsts.INF_INVOKE_ERROR.throwOut(PAY_STATUS_OP_CODE, "支付结果查询接口",
                "支付结果查询接口返回内容为空！");
        }
        // 返回内容
        PayStatusRspDTO payStatusRspDTO = new PayStatusRspDTO();
        Map respMap = JSON.parseObject(resultStr, Map.class);
        Map uniBssBodyResult = MapUtils.getMap(respMap, "UNI_BSS_BODY");
        PayCommonRspDTO payCommonRspDTO = JSONObject.parseObject(JSON.toJSONString(uniBssBodyResult.get("STANDARD_PAY_STATUS_QUERY_RSP")), PayCommonRspDTO.class);
        if ("0000".equals(payCommonRspDTO.getRspCode())) {
            // 解密
            String decodeContent = "";
            try {
                decodeContent = PayUtil.getDecryptContent(payCommonRspDTO.getContent());
                if (!PayUtil.signValid(decodeContent)) {
                    ErrorConsts.BUSI_COMMON_ERROR.throwOut("支付结果查询接口签名串校验异常");
                }
            } catch (Exception e) {
                ErrorConsts.BUSI_COMMON_ERROR.throwOut("支付结果查询接口解密异常：" + e.getMessage());
            }
            payStatusRspDTO = JSONObject.parseObject(decodeContent, PayStatusRspDTO.class);
            String resultCode = payStatusRspDTO.getResultCode();
            String realFee = payStatusRspDTO.getRealFee();
            String payMethod = payStatusRspDTO.getPayMethod();
            String tradeTime = payStatusRspDTO.getTradeTime();
            if (Objects.nonNull(resultCode)) {
                payStatusRspDTO.setResultCode(KiteMapUtils.getString(STATUS_MAP, resultCode));
                // 如果是支付成功了的话，则清楚缓存数据，订单支付流水改为保存十分钟
                if ("SUCCESS".equals(resultCode)) {
                    OneOffFeeExtOrderInfoCache.getInst().defaultDelete(outOrderId);
                    OneOffFeeOrderNoCache.delayPut(outOrderId, orderId);
                }
            }
            if (Objects.nonNull(realFee)) {
                payStatusRspDTO.setRealFee(PayUtil.doubleTrans(Double.parseDouble(realFee)));
            }
            if (Objects.nonNull(payMethod)) {
                DcPublic payMethodConfig = DcPublicCache.getByPkey("2021061091", "PAY_METHOD_CONFIG").stream().filter(config -> KiteStringUtils.equals(payMethod, config.getCodea())).findFirst().orElse(null);
                payStatusRspDTO.setPayMethod(Optional.ofNullable(payMethodConfig).map(DcPublic::getCodeb).orElse(""));
            }
            if (Objects.nonNull(tradeTime)) {
                payStatusRspDTO.setTradeTime(DateFormatUtils.formatDate(DateFormatUtils.parseStrToDate(tradeTime, DateFormatUtils.DATE_TIME_FORMAT_14), DateFormatUtils.DEFAULTPATTERN));
            }
        }
        payStatusRspDTO.setErrorCode(payCommonRspDTO.getRspCode());
        payStatusRspDTO.setErrorMessage(payCommonRspDTO.getRspName());
        return payStatusRspDTO;
    }

    /**
     * 返回缴费确认界面信息
     *
     * @param orderId
     * @return
     */
    @Override
    public PayInfoConfirmDTO payInfoConfirm(String orderId) {
        PayInfoConfirmDTO payInfoConfirmDTO = new PayInfoConfirmDTO();
        // 趸交资费信息
        List<Map<String, Object>> additionalOffers = new ArrayList<>();
        // 合计金额
        double amount = 0d;
        // 订单详情，将数据放到缓存，下单需要用到，下完单就删除
        KitePosDTO kitePosDTO = OneOffFeeExtOrderInfoCache.get(orderId);
        if (Objects.isNull(kitePosDTO)) {
            kitePosDTO = getOrderDetail(orderId);
            OneOffFeeExtOrderInfoCache.getInst().defaultPut(orderId, kitePosDTO);
        }
        List<KitePosGoodsDTO> kitePosGoodsDTO = !KiteListUtils.isEmpty(kitePosDTO.getGoods()) ? kitePosDTO.getGoods() : Lists.newArrayList();
        for (KitePosGoodsDTO goods : kitePosGoodsDTO) {
            List<KitePosGoodsFeeDTO> fees = !KiteListUtils.isEmpty(goods.getFees()) ? goods.getFees() : Lists.newArrayList();
            for (KitePosGoodsFeeDTO fee : fees) {
                Offer offer = ProdFeeCache.get(fee.getCode());
                if(KiteObjectUtils.isEmpty(offer)){
                    localRefreshCacheService.refreshOffer(goods.getCode());
                    offer = ProdFeeCache.get(fee.getCode());
                }
                Map<String, Object> offerMap = Optional.ofNullable(offer).map(Offer::getOfferMap).orElse(null);
                if (!LocalKeyConsts.FEE_PAY_TYPE_ONE_OFF.equals(KiteMapUtils.getString(offerMap, "payType"))) {
                    continue;
                }
                if (!ChangeTypeUtils.CHANGE_TYPE_A.equals(fee.getChangeType())) {
                    continue;
                }
                String totalPrice = getAttrValue(fee.getAttrs(), LocalKeyConsts.PACKAGE_CYCLE_AGREEMENT_PRICE);
                if (KiteStringUtils.isNotEmpty(totalPrice)) {
                    amount = DecimalCalc.add(amount, Double.parseDouble(totalPrice));
                }
                Map<String, Object> additionalOfferMap = new HashMap<>();
                additionalOfferMap.put("offerName", Optional.ofNullable(offer).map(Offer::getOfferName).orElse(""));
                additionalOfferMap.put("totalPrice", totalPrice);
                additionalOffers.add(additionalOfferMap);
            }
        }
        payInfoConfirmDTO.setAdditionalOffers(additionalOffers);
        payInfoConfirmDTO.setAmount(PayUtil.doubleTrans(amount));
        // 支付结果信息
        PayStatusRspDTO payStatusRspDTO = new PayStatusRspDTO();
        payStatusRspDTO.setErrorCode("0000");
        try {
            payStatusRspDTO = this.qryPayStatus(orderId);
        } catch (Exception e) {
            LOGGER.error(e);
        }
        payInfoConfirmDTO.setPayStatus(payStatusRspDTO);
        return payInfoConfirmDTO;
    }

    /**
     * 是否开具发票，传业务号码和终止趸交资费的实例ID
     * @param serialNumber
     * @param feeInstanceId
     * @return
     */
    @Override
    public boolean isIssueInvoice(String serialNumber, String feeInstanceId) {
        boolean result = false;
        List<String> orderIds = this.getOneOffFeeOrderList(serialNumber);
        List<KitePosDetailDTO> kitePosDTOList = this.getOrderDetailByMultiThread(orderIds);
        KitePosDTO targetOrderInfo = this.filterOrderDetail(kitePosDTOList, feeInstanceId);
        if (Objects.nonNull(targetOrderInfo)) {
            result = this.isPrintInvoice(targetOrderInfo);
        }
        return result;
    }

    /**
     * 通过业务号码，查询订单列表（ifPay=1），获取有趸交资费的订单
     * @param serialNumber
     * @return
     */
    private List<String> getOneOffFeeOrderList(String serialNumber) {
        List<String> orderIds = new ArrayList<>();
        OrderInfoReq orderInfoReq = new OrderInfoReq();
        orderInfoReq.setSerialNumber(serialNumber);
        orderInfoReq.setIfPay("1");

        OrderInfoListReq orderInfoListReq = new OrderInfoListReq();
        orderInfoListReq.setPage("1");
        orderInfoListReq.setSize("50"); // TODO: 暂时取五十条吧
        orderInfoListReq.setQueryType("order");
        orderInfoListReq.setOrder(orderInfoReq);

        OrderListInfo orderInfoList = orderService.queryOrderList(orderInfoListReq);
        if ("00000".equals(orderInfoList.getCode())) {
            List<OrderInfo> orderInfos = orderInfoList.getOrderInfoList();
            if (KiteListUtils.isNotEmpty(orderInfos)) {
                orderInfos.forEach(orderInfo -> {
                    orderIds.add(orderInfo.getOrderId());
                });
            }
        } else {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("查询订单列表异常:" + orderInfoList.getMessage());
        }
        return orderIds;
    }

    /**
     * 多线程获取订单信息
     * @param orderIds
     * @return
     */
    private List<KitePosDetailDTO> getOrderDetailByMultiThread(List<String> orderIds) {
        // 多线程获取所有订单信息
        CompletionService<KitePosDetailDTO> service = QryOrderDetailCompletionService.INSTANCE.getExecutorCompletionService();
        List<Future<KitePosDetailDTO>> futureList = new ArrayList<>();
        List<KitePosDetailDTO> kitePosDTOList = new ArrayList<>();
        try {
            for (String orderId : orderIds) {
                QryOrderDetailCallTask task = new QryOrderDetailCallTask(orderId);
                Future<KitePosDetailDTO> future = service.submit(task);
                futureList.add(future);
            }
            for(Future<KitePosDetailDTO> future : futureList){
                KitePosDetailDTO KitePosDTO = future.get();
                kitePosDTOList.add(KitePosDTO);
            }
        } catch (Exception e) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("获取所有订单信息失败:" + e.getMessage());
        }
        return kitePosDTOList;
    }

    /**
     * 找出匹配的订单信息
     * @param kitePosDTOList
     * @param feeInstanceId
     * @return
     */
    private KitePosDetailDTO filterOrderDetail(List<KitePosDetailDTO> kitePosDTOList, String feeInstanceId) {
        KitePosDetailDTO orderInfo = kitePosDTOList.stream().filter(kitePosDTO -> {
            List<KitePosGoodsDTO> goods = kitePosDTO.getGoods();
            for (KitePosGoodsDTO goodsDTO : goods) {
                List<KitePosGoodsFeeDTO> fees = goodsDTO.getFees();
                for (KitePosGoodsFeeDTO fee : fees) {
                    List<KitePosAttrDTO> feeAttrs = fee.getAttrs();
                    KitePosAttrDTO feeInstanceAttr = feeAttrs.stream().filter(feeAttr -> KiteStringUtils.equals(feeAttr.getCode(), "210004688")).findFirst().orElse(null);
                    String instanceId = Optional.ofNullable(feeInstanceAttr).map(KitePosAttrDTO::getValue).orElse("");
                    if (KiteStringUtils.equals(instanceId, feeInstanceId) && ChangeTypeUtils.CHANGE_TYPE_A.equals(fee.getChangeType())) {
                        return true;
                    }
                }
            }
            return false;
        }).findFirst().orElse(null);
        return orderInfo;
    }

    /**
     * 是否打印发票，true已打印，false未打印
     * @param orderInfo
     * @return
     */
    public boolean isPrintInvoice(KitePosDTO orderInfo) {
        boolean result = false;
        if (Objects.nonNull(orderInfo)) {
            List<KitePosPaymentDTO> paymentDTOS = orderInfo.getPayments();
            for (KitePosPaymentDTO paymentDTO : paymentDTOS) {
                List<KitePosAttrDTO> paymentAttrs = paymentDTO.getAttrs();
                KitePosAttrDTO printInvoiceAttr = paymentAttrs.stream().filter(attr -> KiteStringUtils.equals(attr.getCode(), "printInvoice")).findFirst().orElse(null);
                String printInvoice = Optional.ofNullable(printInvoiceAttr).map(KitePosAttrDTO::getValue).orElse("");
                // 0未打印 1已打印 2返销
                if (KiteStringUtils.equals(printInvoice, "1")) {
                    result = true;
                }
            }
        }
        return result;
    }

    /**
     * 校验趸交资费金额
     *
     * @param money
     * @param goodsList
     * @return
     */
    private boolean isEqualAmount(String money, List<KitePosGoodsDTO> goodsList) {
        double amount = 0d;
        for (KitePosGoodsDTO goods : goodsList) {
            List<KitePosGoodsFeeDTO> fees = !KiteListUtils.isEmpty(goods.getFees()) ? goods.getFees() : Lists.newArrayList();
            for (KitePosGoodsFeeDTO fee : fees) {
                Map<String, Object> offerMap = Optional.ofNullable(ProdFeeCache.get(fee.getCode())).map(Offer::getOfferMap).orElse(null);
                if(KiteObjectUtils.isEmpty(offerMap)){
                    localRefreshCacheService.refreshOffer(goods.getCode());
                    offerMap=Optional.ofNullable(ProdFeeCache.get(fee.getCode())).map(Offer::getOfferMap).orElse(null);
                }
                String totalPrice = getAttrValue(fee.getAttrs(), LocalKeyConsts.PACKAGE_CYCLE_AGREEMENT_PRICE);
                if (LocalKeyConsts.FEE_PAY_TYPE_ONE_OFF.equals(KiteMapUtils.getString(offerMap, "payType")) && ChangeTypeUtils.CHANGE_TYPE_A.equals(fee.getChangeType()) && KiteStringUtils.isNotEmpty(totalPrice)) {
                    amount = DecimalCalc.add(amount, Double.parseDouble(totalPrice));
                }
            }
        }
        return KiteStringUtils.equals(money, PayUtil.doubleTrans(amount));
    }

    /**
     * 对端系统趸交配置ONE_OFF_FEE_CONFIG
     *
     * @return
     */
    private DcPublic getOneOffFeeConfig() {
        DcPublic oneOffFeeConfig = DcPublicCache.get("2021061091", "ONE_OFF_FEE_CONFIG");
        if (Objects.isNull(oneOffFeeConfig)) {
            ErrorConsts.BUSI_CHECK_ERROR.throwOut("", "支付下单", "趸交配置为空[2021061091,ONE_OFF_FEE_CONFIG]，请联系运维人员处理。");
        }
        return oneOffFeeConfig;
    }

    private PayContentRspDTO getPayContentRsp(String outOrderId, String url) {
        PayContentRspDTO payContentRspDTO = new PayContentRspDTO();
        payContentRspDTO.setOutOrderId(outOrderId);
        payContentRspDTO.setUrl(url);
        return payContentRspDTO;
    }

    private KitePosDTO getOrderDetail(String orderId) {
        Map<String, Object> orderInfoMap = orderService.getOrderDetail(orderId);
        KitePosDTO kitePosDTO = JSONObject.toJavaObject(JSON.parseObject(JSON.toJSONString(orderInfoMap)), KitePosDTO.class);
        this.removeActivityGoods(kitePosDTO);
        return kitePosDTO;
    }

    private String getAttrValue(List<KitePosAttrDTO> attrs, String fieldName) {
        KitePosAttrDTO attrDTO = attrs.stream().filter(attr -> KiteStringUtils.equals(attr.getCode(), fieldName)).findFirst().orElse(null);
        return Optional.ofNullable(attrDTO).map(KitePosAttrDTO::getValue).orElse("");
    }

    private void removeActivityGoods(KitePosDTO kitePosDTO) {
        kitePosDTO.getGoods().removeIf(
            goods -> goods.getProducts().stream().anyMatch(prod -> KiteStringUtils.equals(prod.getType(), "discount")));
    }

    private String getDepartId() {
        Long departId = 0L;
        List<PostInfo> postInfos = ContextUtil.getLoginInfo().getPostInfos();
        if (KiteListUtils.isNotEmpty(postInfos)) {
            departId = Optional.ofNullable(postInfos.get(0)).map(PostInfo::getPostId).orElse(0L);
        }
        return String.valueOf(departId);
    }

    private String getOfferId(KitePosDTO kitePosDTO) {
        String offerId = "";
        if (Objects.nonNull(kitePosDTO)) {
            offerId = Optional.ofNullable(kitePosDTO.getGoods().get(0)).map(KitePosGoodsDTO::getCode).orElse("");
        }
        return offerId;
    }

    private String getBaseProdId(KitePosDTO kitePosDTO) {
        String prodId = "";
        if (Objects.nonNull(kitePosDTO)) {
            for (KitePosGoodsDTO kitePosGoodsDTO : kitePosDTO.getGoods()) {
                for (KitePosProductDTO kitePosProductDTO : kitePosGoodsDTO.getProducts()) {
                    if (KiteStringUtils.equals(kitePosProductDTO.getType(), "base")) {
                        prodId = kitePosProductDTO.getCode();
                    }
                }
            }
        }
        return prodId;
    }

    @Override
    public void resaleCheck(ResaleOrderRequest resaleOrderRequest, KitePosDTO orderKitePosDTO, KitePosDTO userKitePosDTO) {
        // 1. 当前受理工号归属与原单趸交活动受理工号归属不一致，不允许发起活动返销
        if (!KiteStringUtils.equals(resaleOrderRequest.getStaffCode(), orderKitePosDTO.getAccept().getCode())) {
            // 如果不同，通过根据集客工号查询对应的组织、区域信息接口查询工号对应得渠道编码。两个工号对应的渠道编码相同可以受理
            String currChannelCode = this.getChannelCode(resaleOrderRequest.getStaffCode());
            String oldChannelCode = this.getChannelCode(orderKitePosDTO.getAccept().getCode());
            if (!KiteStringUtils.equals(currChannelCode, oldChannelCode)) {
                ErrorConsts.BUSI_COMMON_ERROR.throwOut("当前受理工号归属与原单趸交活动受理工号归属不一致，不允许发起活动返销");
            }
        }
        // 4. 此订单已返销，不允许再次进行返销（12是返销中，13是已返销）
        if (KiteStringUtils.equalsAny(resaleOrderRequest.getStatus(), "12", "13")) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("此订单已返销，不允许再次进行返销");
        }
        // 3. 发票已经打印，不允许进行返销
        if (this.isPrintInvoice(orderKitePosDTO)) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("发票已经打印，不允许进行返销");
        }
        // 2. 趸交资费已生效，无法进行趸交返销(判断该订单中所有新增的趸交资费都未生效)
        if (this.existsEffFee(orderKitePosDTO, userKitePosDTO)) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("趸交资费已生效，无法进行趸交返销");
        }
    }

    /**
     * 根据业务号码，资费实例id，获取添加该资费的订单id
     * @author zhang.song
     * @date 2022-03-28 18:57
     * @param serialNumber
     * @param feeInstanceIds
     * @return java.util.List<java.util.Map<java.lang.String,java.lang.String>>
     */
    @Override
    public List<Map<String, String>> getFeeAddOrderId(String serialNumber, List<String> feeInstanceIds) {
        List<Map<String, String>> result = new ArrayList<>();
        if(KiteStringUtils.isNotEmpty(serialNumber) && KiteListUtils.isNotEmpty(feeInstanceIds)) {
            List<String> orderIds = this.getOneOffFeeOrderList(serialNumber);
            List<KitePosDetailDTO> kitePosDTOList = this.getOrderDetailByMultiThread(orderIds);
            Map<String, String> feeInstIdAndOrderIdMap;
            KitePosDetailDTO targetOrderInfo;
            for (String feeInstanceId : feeInstanceIds) {
                targetOrderInfo = this.filterOrderDetail(kitePosDTOList, feeInstanceId);
                if(Objects.nonNull(targetOrderInfo)) {
                    feeInstIdAndOrderIdMap = new HashMap<>(4);
                    feeInstIdAndOrderIdMap.put("feeInstanceId", feeInstanceId);
                    feeInstIdAndOrderIdMap.put("orderId", targetOrderInfo.getOrderId());
                    result.add(feeInstIdAndOrderIdMap);
                }
            }
        }
        return result;
    }

    private boolean existsEffFee(KitePosDTO orderKitePosDTO, KitePosDTO userKitePosDTO) {
        List<KitePosGoodsFeeDTO> effFeeList = new ArrayList<>();
        // 获取该订单所有新增的趸交资费
        List<KitePosGoodsFeeDTO> orderFeeList = getOrderFee(orderKitePosDTO, ChangeTypeUtils.CHANGE_TYPE_A);
        // 获取存量的趸交资费
        List<KitePosGoodsFeeDTO> stockFeeList = getOrderFee(userKitePosDTO);
        if (KiteListUtils.isEmpty(orderFeeList)) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("获取不到该订单新增的趸交资费，不允许进行返销");
        }
        if (KiteListUtils.isEmpty(stockFeeList)) {
            ErrorConsts.BUSI_COMMON_ERROR.throwOut("获取不到该用户已存在的趸交资费，不允许进行返销");
        }
        // 用该竣工订单中新增的趸交资费对应在存量数据的生效时间来判断是否生效
        stockFeeList.stream().filter(stockFee -> {
            String feeInstanceId = this.getFeeInstanceId(stockFee);
            return orderFeeList.stream().anyMatch(orderFee -> KiteStringUtils.equals(feeInstanceId, this.getFeeInstanceId(orderFee)));
        }).forEach(fee -> {
            // 判断是否生效
            String effDateTime = Optional.ofNullable(fee.getAttrs().stream().filter(attr ->
                KiteStringUtils.equals(attr.getCode(), LocalKeyConsts.STARTING_DATE_FIELD_NAME)).findFirst().orElse(null)).map(KitePosAttrDTO::getValue).orElse("");
            if (KiteStringUtils.isNotEmpty(effDateTime) && DateFormatUtils.compareDate(DateFormatUtils.getFormatedDateTime(), effDateTime, DateFormatUtils.DATE_TIME_FORMAT) > 0) {
                effFeeList.add(fee);
            }
        });
        // 获取该笔订单中失效的新增趸交资费，如果在存量数据中查不到，则是已失效（失效资费用户中心不返回）
        effFeeList.addAll(orderFeeList.stream().filter(orderFee -> {
            String feeInstanceId = this.getFeeInstanceId(orderFee);
            return !stockFeeList.stream().anyMatch(stockFee -> KiteStringUtils.equals(this.getFeeInstanceId(stockFee), feeInstanceId));
        }).collect(Collectors.toList()));
        return KiteListUtils.isNotEmpty(effFeeList);
    }

    private static List<KitePosGoodsFeeDTO> getOrderFee(KitePosDTO orderKitePosDTO) {
        return getOrderFee(orderKitePosDTO, null);
    }

    public static List<KitePosGoodsFeeDTO> getOrderFee(KitePosDTO orderKitePosDTO, String changeType) {
        List<KitePosGoodsFeeDTO> feeDTOList = new ArrayList<>();
        if (Objects.isNull(orderKitePosDTO)) {
            return feeDTOList;
        }
        orderKitePosDTO.getGoods().stream().forEach(goods -> {
            if (KiteStringUtils.isNotEmpty(changeType)) {
                feeDTOList.addAll(goods.getFees().stream().filter(fee -> KiteStringUtils.equals(fee.getChangeType(), changeType)).collect(Collectors.toList()));
            } else {
                Optional.ofNullable(goods.getFees()).ifPresent(feeDTOList::addAll);
            }
        });
        // 保留趸交资费
        feeDTOList.removeIf(feeDTO -> {
            Offer offer = ProdFeeCache.get(feeDTO.getCode());
            Map<String, Object> offerMap = Optional.ofNullable(offer).map(Offer::getOfferMap).orElse(null);
            if (!LocalKeyConsts.FEE_PAY_TYPE_ONE_OFF.equals(KiteMapUtils.getString(offerMap, "payType"))) {
                return true;
            }
            return false;
        });
        return feeDTOList;
    }

    private String getChannelCode(String staffCode) {
        QryGovUserCodeReqDTO qryGovUserCodeReqDTO = new QryGovUserCodeReqDTO();
        qryGovUserCodeReqDTO.setUserCode(staffCode);
        GovUserCodeRspDTO govUserCodeRspDTO = infServiceBus.qryGovUserInfo(qryGovUserCodeReqDTO);
        return Optional.ofNullable(govUserCodeRspDTO.getUserInfo()).map(UserInfoDTO::getChnlCode).orElse("");
    }

    private String getFeeInstanceId(KitePosGoodsFeeDTO kitePosGoodsFeeDTO) {
        return Optional.ofNullable(kitePosGoodsFeeDTO.getAttrs().stream().filter(s -> KiteStringUtils.equals(s.getCode(), "210004688"))
            .findFirst().orElse(null)).map(KitePosAttrDTO::getValue).orElse("");
    }
}
